﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LockCrypt.Core;
using System.IO;

namespace LockCrypt.DAL.Exporters {
    /// <summary>
    /// Exports a CSV file.
    /// </summary>
    public class CSVExporter : IDatabaseExporter {
        /// <summary>
        /// Gets or sets the logger to use.
        /// </summary>
        /// <value>The logger.</value>
        public ILogProvider Logger { get; set; }

        /// <summary>
        /// Gets or sets the filename to which to write.
        /// </summary>
        /// <value>The name of the file to which to write.</value>
        public string FileName { get; set; }

        /// <summary>
        /// Initializes a new <see cref="CSVExporter"/>.
        /// </summary>
        /// <param name="fileName">Name of the file to which to write.</param>
        public CSVExporter(string fileName) {
            Logger = new NullLogger();
            this.FileName = fileName;
        }

        /// <summary>
        /// Exports the database.
        /// </summary>
        /// <param name="contents">The contents to export.</param>
        public void ExportDatabase(DatabaseContents contents) {
            Dictionary<string, ITemplate> templateIdMap;
            if(contents.Templates == null) {
                templateIdMap = new Dictionary<string, ITemplate>();
            } else {
                templateIdMap = contents.Templates.ToDictionary(t => t.ID);
            }
            var groupIdMap = contents.Groups.ToDictionary(g => g.ID); // <group ID, group>
            var dummyGroup = new GroupBase() { Name = I18nUtils.GetString("Strings", "None") };
            var accountGroups = contents.Accounts.OrderBy(a=>a.Name)
                                                 .GroupBy(a => a.ParentID ?? "-1")
                                                 .ToDictionary(a => a.Key != "-1" && groupIdMap.ContainsKey(a.Key) ? groupIdMap[a.Key] : dummyGroup, g => g.ToList());
            
            Logger.Log(string.Format("{0} accounts, {1} groups to export", contents.Accounts.Count, contents.Groups.Count), StatusMessage.StatusMessageType.Status);
            using(FileStream fs = new FileStream(FileName, FileMode.Create, FileAccess.Write)) 
            using(StreamWriter writer = new StreamWriter(fs, Encoding.UTF8)) {
                writer.WriteLine(@"Name,Template Name, Group, Icon, Notes, Created UTC, Modified UTC, Fields");
                foreach(KeyValuePair<IGroup, List<IAccount>> pair in accountGroups) {
                    foreach(var account in pair.Value) {
                        string[] values;
                        string templateName = null;
                        if(account.Template != null) {
                            if(account.Template.Name != null) {
                                templateName = account.Template.Name;
                            } else if(account.Template.ID != null && templateIdMap.ContainsKey(account.Template.ID)) {
                                templateName = templateIdMap[account.Template.ID].Name;
                            }
                        }
                        values = new[]
                                         {
                                             account.Name,
                                             templateName,
                                             pair.Key.Name,
                                             account.Icon,
                                             account.Notes,
                                             account.Created.ToUniversalTime().ToString("u"),
                                             account.Modified.ToUniversalTime().ToString("u")
                                         };
                        writer.WriteLine(values.Aggregate((items, item) => items + "," + (string.IsNullOrEmpty(item) ? string.Empty : string.Format(item.Contains(',') ? "\"{0}\"" : "{0}", item))) // escape commas with quotes
                               + account.Fields.Aggregate(string.Empty, (fields, field) => string.Format(field.Name.Contains(',') || (field.Value ?? string.Empty).Contains(',') ?
                                                                                              "{0},\"{1}:{2}\"" : "{0},{1}:{2}", fields, field.Name, field.Value ?? string.Empty)));
                        Logger.Log(string.Format("Exported {0}", account.Name, StatusMessage.StatusMessageType.Success));
                    }
                }
            }
        }
    }
}
